# encoding: utf8


"""
模块docx的对象Document及其生成.docx文档的应用方式

Document
    paragraphs: [ paragraph, ... ]
        paragraph
            runs:[run, ... ]
                run

    tables: [table, ... ]
        table
        .rows: [row, ... ]
            row
            .cells: [ cell, ... ]
                cell
                .width,
                .text,
                .paragraphs,
                .tables
        .columns: [column, ... ]
            column
            .cells: [cell, ... ]
                cell
                .width, .text, .paragraphs, .tables

    styles: iterable object including style-object
        all styles for each object in Document
        for style in styles:
            print(style)
        .element.xml

    inline_shapes: [inline_object, ... ]
        .part.element.xml

    # 导入
    from docx.enum.text import WD_ALIGN_PARAGRAPH   # 文本对齐
    from docx.oxml.ns import qn
    from docx.shared import RGBColor

    # 设置段落格式
    # -- 段落、段程文字对齐
    ph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
    run.alignment = WD_PARAGRAPH_ALIGNMENT.JUSTIFY
        对齐可选项有：
        1）'LEFT', 0, 'left', 'Left-aligned'
        2）'CENTER', 1, 'center', 'Center-aligned.'
        3）'RIGHT', 2, 'right', 'Right-aligned.'
        4）'JUSTIFY', 3, 'both', 'Fully justified.'
        5）'DISTRIBUTE', 4, 'distribute',
           'Paragraph characters are distributed to fill the entire width of the paragraph.'
        6）'JUSTIFY_MED', 5, 'mediumKashida',
           'Justified with a medium character compression ratio.'
        7）'JUSTIFY_HI', 7, 'highKashida',
           'Justified with a high character compression ratio.'
        8）'JUSTIFY_LOW', 8, 'lowKashida',
           'Justified with a low character compression ratio.'
        9）'THAI_JUSTIFY', 9, 'thaiDistribute',
           'Justified according to Thai formatting layout.'
    # -- 段落行间距
    ph.paragraph_format.line_spacing = Pt(20) # 20 磅
    ph.paragraph_format.line_spacing = 1.5 # 1.5倍行距 单倍行距 1.0
        # 当line_spacing设置为长度值时，表示绝对距离，设置为浮点数时表示行高的倍数
    # -- 段前、段后间距
    ph.paragraph_format.space_before = Pt(5)
    ph.paragraph_format.space_after = Pt(10)
    # -- 段落内部文字，需分页时的处理
    ph.paragraph_format.keep_together = True        # 段中不分页
    ph.paragraph_format.keep_with_next = True       # 与下段同页
    ph.paragraph_format.page_break_before = True    # 段前分页
    ph.paragraph_format.widow_control = True        #
    # 获取段落的左缩进，首行缩进
    ph.paragraph_format.left_indent = Pt()
    ph.paragraph_format.first_line_indent = Pt()

    Run对象的text属性：
    # -- 字体格式
    run.font.name = 'Times New Roman'     # 注：这个好像只能用于设置 run 中的西文字体
    # --- 中文字体，需导入 qn 模块
    run.font.element.rPr.rFonts.set(qn('w:eastAsia'),'楷体')
    # -- 字体大小
    run.font.size = Pt(14)
    # -- 字体加粗
    run.font.bold = True
    # -- 字体斜体
    run.font.italic = True
    # -- 字体颜色， 需导入 rgb 颜色模块 RGBColor
    run.font.color.rgb = RGBColor(255,55,55) # 红色
    # -- 字体下划线
    # run.font.underline = True, one of |None|, |True|, |False|
    # -- 字体轮廓线
    # run.font.outline = True
    # -- 字体阴影
    # run.font.shadow = True
    # -- 字体删除线
    # run.font.strike = True
    # -- 字体双删除线
    # run.font.double_strike = True
    # -- 字体下标
    # run.font.subscript = True
    # -- 字体上标
    # run.font.superscript = True
    # -- 文本以大写首字母出现
    run.font.all_caps
    # --  文本以大写首字母出现，小写字母小两个点
    run.font.small_caps
    # -- 文本以轮廓线出现，而不是实心
    run.font.outline
    # -- 文本从右至左书写
    run.font.rtl
    # -- 文本以刻入页面的方式出现
    run.font.imprint
    # -- 文本以凸出页面的方式出现
    run.font.emboss

    # 表格设置
    # 创建表格 add table
    tb = doc.add_table(rows=0,cols=0)
    # -- set table format：alignment
    tb.alignment = WD_TABLE_ALIGNMENT.CENTER
    tb.style='Light Shading'

    # -- 设置整个表格字体属性
    table.style.font.size=Pt(18)
    table.style.font.color.rgb=RGBColor(255, 0, 0)
    table.style.paragraph_format.alignment=WD_PARAGRAPH_ALIGNMENT.CENTER

    # 创建列、行，设置宽高 add column, set width、height
    col = tb.add_column(width=docx.shared.Cm(3))  # 宽度为厘米
    row = tb.add_row()                            # 无初始化参数
    row.height = docx.shared.Cm(1)                # 行高1厘米

    # -- 合并单元格
    cell_1=table.cell(1, 0)
    cell_2=table.cell(2, 1)
    cell_1.merge(cell_2)

    表格数据写入
    # -- 方法一（直接写入第一段落第一段程）
    cell=table.cell(0, 1)
    cell.text = ‘i-search’
    # -- 方法二（指定段落、段程）
    cell=table.cell(0,0)
    cph = cell.paragraphs[0]
    run = cph.add_run(“RPA”)

    表格文本格式设置
    # -- 单个单元格设置
    run.font.color.rgb = RGBColor(255, 0, 0)
    run.font.size = Pt(15)
    # -- 设置水平对齐方式（LEFT，RIGHT，CENTER）
    cph.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
    # -- 设置垂直对齐方式（TOP，BOTTOM，CENTER）
    cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER

    # 加入图片
    doc.add_picture(image_path_or_stream, width=None, height=None)
    :image_path_stream 图像路径文件名或流数据，加入文件时支持png，不支持jpg格式
    :width, height 图片在文档中的宽度和高度，设为None时自适应
    pic_par = doc.add_picture('1.png')             # 返回一个 InlineShape 对象

    # 读取图片数据
    rid = doc.inline_shapes[0]._inline.graphic.graphicData.pic.blipFill.blip.embed
    image_data = doc.part.related_parts[rid]._blob
"""


import os
import docx
from win32com.client import Dispatch


def create_docx(docfile='demo.docx'):
    """
    开创包含标题、段落、区块、插图、表格的.docx文档的示例代码

    >>> create_docx('demo.docx')
    >>> dr = DocxReader('demo.docx')
    >>> dr.get_paragraphs()
    [['辛弃疾'], ['原字坦夫，后改字幼安，中年后别号稼轩居士，山东济南府历城县（今山东省济南市历城区遥墙镇四风闸村）人。\
南宋官员、将领、文学家，豪放派词人，有“词中之龙”之称。与苏轼合称“苏辛”，与李清照并称“济南二安”。'], \
['最著名的词作\\n破阵子·为陈同甫赋壮词以寄之'], ['上阕: ', '醉里挑灯看剑，梦回吹角连营。八百里分麾下炙，五十弦翻塞外声。\
沙场秋点兵。'], ['下阙: ', '马作的卢飞快，弓如霹雳弦惊。了却君王天下事，赢得生前身后名。可怜白发生！'], ['']]
    >>> dr.get_tables()
    [[['年份', '年龄', '事项'], ['宋绍兴十年（1140）', '出生', '生于山东历城之四风闸'], \
['宋绍兴三十一年（1161）', '22岁', '金主亮大举南犯，稼轩聚众二千，与耿京共图恢复。'], \
['宋绍兴三十二年（1162）', '23岁', '奉表南归。十八日至建康。召见，授右承务郎。'], \
['宋淳熙十五年（1188）', '49岁', '友人陈同甫（亮）来访，长歌相答，作破阵子。'], \
['宋庆元二年（1196）', '57岁', '以纠结徒党罪名罢斥朱熹及其门徒。'], \
['宋嘉泰四年（1204）', '65岁', '支持韩侂胄发动对金战争。'], \
['宋开禧三年（1207）', '68岁', '归铅山，八月得疾, 九月初十日卒。']]]
    >>> dr.get_pictures('temppath')
    >>> os.listdir('temppath')
    ['image_001.png']

    # >>> fs = os.listdir('temppath')
    # >>> for f in fs: os.remove('temppath/'+f)
    # >>> os.removedirs('temppath')

    """
    from docx import shared
    from docx.enum.table import WD_TABLE_ALIGNMENT
    from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
    from docx.shared import Pt, RGBColor
    from docx.oxml.ns import qn

    doc = docx.Document()

    # set font
    doc.styles['Normal'].font.name = '宋体'
    doc.styles['Normal'].element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    doc.styles['Normal'].font.size = Pt(10.5)   # 5号字体相当 抄 于 10.5pt
    doc.styles['Normal'].font.color.rgb = RGBColor(0, 0, 0)

    # add headers
    phh = doc.add_heading('辛弃疾', level=1)
    phh.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
    phh.runs[0].font.color.rgb = RGBColor(100, 100, 0)
    phh.runs[0].font.name = '黑体'
    phh.runs[0].element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')

    # add paragraph
    ph0 = doc.add_paragraph()
    ph0.paragraph_format.space_before = Pt(8)
    run01 = ph0.add_run('原字坦夫，后改字幼安，中年后别号稼轩居士，山东济南府历城县'
                        '（今山东省济南市历城区遥墙镇四风闸村）人。'
                        '南宋官员、将领、文学家，豪放派词人，有“词中之龙”之称。'
                        '与苏轼合称“苏辛”，与李清照并称“济南二安”。')
    run01.font.name = '仿宋'
    run01.element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋')
    run01.alignment = WD_PARAGRAPH_ALIGNMENT.JUSTIFY
    ph0.paragraph_format.first_line_indent = Pt(21)

    # add heading
    phh2 = doc.add_heading('最著名的词作\n破阵子·为陈同甫赋壮词以寄之', level=2)
    phh2.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
    phh2.runs[0].font.color.rgb = RGBColor(255, 10, 10)
    phh2.runs[0].font.name = '楷体'
    phh2.runs[0].element.rPr.rFonts.set(qn('w:eastAsia'), '楷体')
    phh2.paragraph_format.space_after = Pt(10)

    # add paragraph
    p1 = doc.add_paragraph()
    r11 = p1.add_run('上阕: ')
    r11.bold = True
    r12 = p1.add_run('醉里挑灯看剑，梦回吹角连营。八百里分麾下炙，五十弦翻塞外声。沙场秋点兵。')
    r12.font.color.rgb = RGBColor(105, 139, 34)    # OlivDrab4
    r12.font.name = 'Cambria'
    r12.element.rPr.rFonts.set(qn('w:eastAsia'), 'Cambria')
    r12.bold = True
    p2 = doc.add_paragraph()
    r21 = p2.add_run('下阙: ')
    r21.bold = True
    r22 = p2.add_run('马作的卢飞快，弓如霹雳弦惊。了却君王天下事，赢得生前身后名。可怜白发生！')
    r22.font.color.rgb = RGBColor(0, 100, 0)    # DarkGreen
    r22.font.name = '仿宋'
    r22.element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋')
    r22.bold = True

    # add picture,
    # -- add paragraph, alignment to center
    paragraph = doc.add_paragraph()
    paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
    run = paragraph.add_run()
    # -- set picture in run, set picture-path from __file__
    _path = os.path.split(os.path.abspath(__file__))[0]
    run.add_picture(_path+'/xin.png', width=shared.Cm(5), height=shared.Cm(6))

    # add table
    row_count, col_count = 8, 3
    tb = doc.add_table(0, 0, style='Light Shading')
    # -- set table format：alignment
    tb.alignment = WD_TABLE_ALIGNMENT.CENTER
    # -- add columns, set columns width
    tb.add_column(width=shared.Cm(3))
    tb.add_column(width=shared.Cm(2))
    tb.add_column(width=shared.Cm(5))
    # -- add row as table heading, set height
    row = tb.add_row()
    row.height = docx.shared.Cm(0.5)
    # -- add other rows
    for _ in range(row_count-1):
        row = tb.add_row()
        row.height = docx.shared.Cm(1)
    # -- set table header
    table_header = ['年份', '年龄', '事项']
    for j, cell in enumerate(tb.rows[0].cells):
        cell.text = table_header[j]
        cell.paragraphs[0].paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        cell.paragraphs[0].runs[0].bold = True
    # -- set cell.text, cell.paragraphs[].font, bold
    content = [['宋绍兴十年（1140）', '出生', '生于山东历城之四风闸'],
               ['宋绍兴三十一年（1161）', '22岁', '金主亮大举南犯，稼轩聚众二千，与耿京共图恢复。'],
               ['宋绍兴三十二年（1162）', '23岁', '奉表南归。十八日至建康。召见，授右承务郎。'],
               ['宋淳熙十五年（1188）', '49岁', '友人陈同甫（亮）来访，长歌相答，作破阵子。'],
               ['宋庆元二年（1196）', '57岁', '以纠结徒党罪名罢斥朱熹及其门徒。'],
               ['宋嘉泰四年（1204）', '65岁', '支持韩侂胄发动对金战争。'],
               ['宋开禧三年（1207）', '68岁', '归铅山，八月得疾, 九月初十日卒。'],
               ['', '', '']
               ]
    for i in range(len(content)):
        if i > row_count-2:
            break
        for j in range(3):
            tb.cell(i+1, j).text = content[i][j]
        tb.cell(i+1, 1).paragraphs[0].runs[0].font.color.rgb = RGBColor(0, 0, 250)
        tb.cell(i+1, 1).paragraphs[0].paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        tb.cell(i+1, 2).paragraphs[0].runs[0].font.name = '楷体'
        tb.cell(i+1, 2).paragraphs[0].runs[0].element.rPr.rFonts.set(qn('w:eastAsia'), '楷体')
        tb.cell(i + 1, 2).paragraphs[0].runs[0].bold = True

    # save docx
    doc.save(docfile)


class DocxReader:
    """
    读出.docx文档的文本、表格、图片
    """

    def __init__(self, docxfile='demo.docx'):
        self.doc = docx.Document(docxfile)

    def get_paragraphs(self):
        """
        读取文档各段落的文本
        返回文档文本列表，元素为每段落各段程文本的列表
        """
        doctext = []
        for ph in self.doc.paragraphs:
            text = []
            for run in ph.runs:
                text.append(run.text)
            doctext.append(text)
        return doctext

    def get_tables(self):
        """
        读取文档各表格的文本
        返回文档文本列表，元素为每表格各行各单元格文本列表的列表
        """
        tables = self.doc.tables
        tables_text = []
        for table in tables:
            text = []
            for row in table.rows:
                text.append([cell.text for cell in row.cells])
            tables_text.append(text)
        return tables_text

    def get_pictures(self, save_pictures_path='temp_images'):
        """
        读取文档中的图片数据
        返回写入临时目录中的图像文件名的列表
        """
        if not os.path.isdir(save_pictures_path):
            os.mkdir(save_pictures_path)
        images = []
        save_pictures_path = os.path.abspath(save_pictures_path) + '\\'
        for j in range(len(self.doc.inline_shapes)):
            rID = self.doc.inline_shapes[0]._inline.graphic.graphicData.pic.blipFill.blip.embed
            image = self.doc.part.related_parts[rID]
            image_file = save_pictures_path + 'image_{:03d}.png'.format(j + 1)
            with open(image_file, 'wb') as fp:
                fp.write(image._blob)
            images.append(image_file)
        # return images


def doc_to_docx(docfile, docxfile):
    """
    转换 .doc 文件为 .docx
    :param docfile: doc文档路径文件名
    :param docxfile: docx文档路径文件名
    note:
    (1) 将路径文件名转为绝对路径，否则会出现错误:
        pywintypes.com_error: (-2147352567, '发生意外。', (0, 'Kingsoft WPS', '文档打开失败。', '', 3010, -786427), None)
    (2) 在doc.SaveAs中使用参数16表示doc转换docx
    (3) 如果批量调用本函数，可以将 import win32com 移到模块顶端，同时在Dispatch对象创建后建立循环
    """
    import win32com
    word = win32com.client.Dispatch("Word.Application")
    doc = word.Documents.Open(os.path.abspath(docfile))
    doc.SaveAs(os.path.abspath(docxfile), 16)
    doc.Close()
    word.Quit()


# syntax read from XML
def test_read_pictures_from_docx(docfile):
    """
    通过解析docxXML，读出图形对象
    :param docfile:
    :return:
    """
    import re
    import xml.etree.cElementTree as ET

    doc = docx.Document(docfile)

    proxy = []
    for p in doc.paragraphs:
        proxy.append(p._element.xml)

    for p in proxy:
        # 一段一个根树
        root = ET.fromstring(p)
        # 获得<w:r>树，所有的 <w:pict> 树均是 <w:r> 树的子树
        pictr_str = "%sr" % re.match('{\S+}', root.tag).group(0)
        # print(pictr_str)
        pictrs = root.findall(pictr_str)
        pict_str = "%spict" % re.match('{\S+}', root.tag).group(0)
        # print(pict_str)
        picts = []
        for pictr in pictrs:
            # 获得所有 <w:pict> 标签
            pict = pictr.findall(pict_str)
            print(pict)
            # 如 <w:pict> 存在
            if len(pict) > 0:
                picts.append(pict[0])
        rIds = []
        for pict in picts:
            shape_str = "%sshape" % re.match('{\S+}', pict[0].tag).group(0)
            print(shape_str)
            # 获得<v:shape>标签
            shape=pict.findall(shape_str)[0]
            # attrib=[]
            # <w:imagedata>标签
            imagedata=shape.findall("%simagedata" % re.match('{\S+}',pict[0].tag).group(0))
            rIds.append(imagedata[0].attrib[
                            '{http://schemas.openxmlformats.org/officeDocument/2006/relationships}id'])
        imgs = []
        for rid in rIds:
            img = doc.part.related_parts[rid]
            imgs.append(img)
            print('there is image {}'.format(img))
            # with open("img_%03d.jpg" % i, 'wb') as fp:
                # fp.write(img.blob)
